//
//  JCObjectSerializer.m
//  JCSDK
//
//  Created by master on 16/2/23.
//  Copyright © 2016年 AdMaster. All rights reserved.
//

#import "JCObjectSerializerContext.h"

#import "JCObjectSerializer.h"
#import "JCObjectIdentityProvider.h"
#import "JCSequenceGenerator.h"
#import "JCObjectSerializerConfig.h"
#import "JCClassDescription.h"
#import "JCEnumDescription.h"
#import "JCPropertyDescription.h"

#import "UIView+MPHelpers.h"
#import "NSInvocation+MPHelpers.h"

#import <objc/runtime.h>


@interface JCObjectSerializer ()
{
    JCObjectIdentityProvider *_objectIdentifyProvider;
    JCObjectSerializerConfig *_configuration;
}
@end

@implementation JCObjectSerializer

- (instancetype)initWithConfiguration:(JCObjectSerializerConfig *)configuration objectIdentifyProvider:(JCObjectIdentityProvider *)objectIdentifyProvider {
    if(self = [super init]) {
        _objectIdentifyProvider = objectIdentifyProvider;
        _configuration = configuration;

    }
    return self;
}

- (NSDictionary *)objectHierarchyForWindowAtIndex:(NSUInteger)index
{
    UIWindow *window = [self windowAtIndex:index];
    if (window) {
        return [self serializedObjectsWithRootObject:window];
    }
    
    return @{};
}

- (UIWindow *)windowAtIndex:(NSUInteger)index
{
    NSParameterAssert(index < [[UIApplication sharedApplication].windows count]);
    return [UIApplication sharedApplication].windows[index];
}




- (NSDictionary *)serializedObjectsWithRootObject:(id)rootObject {
    NSParameterAssert(rootObject != nil);
    JCObjectSerializerContext *serializerContext = [[JCObjectSerializerContext alloc] initWithRootObject:rootObject];
    
    while ([serializerContext hasUnvisitedObjects]) {
        NSObject *object = [serializerContext popUnvisitedObject];
        [self serializeObject:object withContext:serializerContext];
    }
    return @{
             @"objects" : [serializerContext allSerializedObjects],
             @"rootObject" : [_objectIdentifyProvider identifierForObject:rootObject]
             };
}

//序列号object
- (void)serializeObject:(NSObject *)obj withContext:(JCObjectSerializerContext *)context {
    NSParameterAssert(obj != nil);
    NSParameterAssert(context != nil);
    [context addVisitedObject:obj];
    NSMutableDictionary *propertyValues = [NSMutableDictionary dictionary];
    JCClassDescription *classDescription = [self classDescriptionForObject:obj];

    // get property value
    if (classDescription) {
        for(JCPropertyDescription *propertyDescription in [classDescription propertyDescriptions]) {
            if([propertyDescription shouldReadPropertyValueForObject:obj]) {
                id propertyValue = [self propertyValueForObject:obj withPropertyDescription:propertyDescription context:context];
                propertyValues[propertyDescription.name] = propertyValue ?:[NSNull null];
            }
            
        }
    }
    
    //TODO: (完成)代理信息捕获
    NSMutableArray *delegateMethods = [NSMutableArray array];
    id delegate;
    SEL delegateSelector = NSSelectorFromString(@"delegate");
    
    if(classDescription && [[classDescription delegateInfos] count] > 0 && [obj respondsToSelector:delegateSelector]) {
        delegate = ((id (*)(id, SEL))[obj methodForSelector:delegateSelector])(obj, delegateSelector);
        for (JCDelegateInfo *delegateInfo  in [classDescription delegateInfos]) {
            if([delegate respondsToSelector:NSSelectorFromString(delegateInfo.selector)]) {
                [delegateMethods addObject:delegateInfo.selector];
            }
        }
    }
    
    NSDictionary *serializedObject = @{
                                       @"id": [_objectIdentifyProvider identifierForObject:obj],
                                       @"class": [self classHierarchyArrayForObject:obj],
                                       @"properties": propertyValues,
                                       @"delegate": @{
                                           @"class": delegate ? NSStringFromClass([delegate class]) : @"",
                                           @"selectors": delegateMethods
                                           }
                                       };
    [context addSerializedObject:serializedObject];

//

}
// 获取类属性 值
- (id)propertyValueForObject:(NSObject *)object withPropertyDescription:(JCPropertyDescription *)propertyDescription context:(JCObjectSerializerContext *)context {
//    NSLog(@"%@.%@  %@",NSStringFromClass([object class]),propertyDescription.name,propertyDescription.type);
    JCSelectorDescription * selectorDescription = propertyDescription.getSelectorDescription;
    NSMutableArray *values = [NSMutableArray array];
    if(propertyDescription.useKVC) {
        id valueForKey = [object valueForKey:selectorDescription.selector];
        id value = [self propertyValueTransform:valueForKey WithPropertyDescription:propertyDescription context:context];
        NSDictionary *valueDictionary = @{
                                          @"value" : (value ?:[NSNull null])
                                          };
        [values addObject:valueDictionary];
    }
    else if(propertyDescription.useInstanceVariableAccess) {
        //变量方法获取Value
        id ivarValue = [self valueWithIvarForObject:object withPropertyDescription:propertyDescription context:context];
        id value = [self propertyValueTransform:ivarValue WithPropertyDescription:propertyDescription context:context];
        NSDictionary *valueDictionary = @{
                                          @"value" : (value ?:[NSNull null])
                                          };
        [values addObject:valueDictionary];
    }
    else {
        
        NSLog(@"%@",propertyDescription.name);
        
        NSInvocation *invocation = [self invocationForObject:object withSelectorDescription:selectorDescription];
        if(invocation) {
            NSArray *parametersVariations = [self parameterVariationsForPropertySelector:selectorDescription];
            
//            NSLog(@"%@",parametersVariations);
            for (NSArray *parameters in parametersVariations) {

                [invocation mp_setArgumentsFromArray:parameters];
                [invocation invokeWithTarget:object];
                id returnValue = [invocation mp_returnValue];
                id value = [self propertyValueTransform:returnValue WithPropertyDescription:propertyDescription context:context];
                NSDictionary *valueDictionary = @{
                                                  @"where": @{ @"parameters" : parameters },
                                                  @"value": (value ?: [NSNull null])
                                                  };
                [values addObject:valueDictionary];
            }
        }
    }
    return @{@"values": values};
}
//类属性值 Transform
- (id)propertyValueTransform:(id)propertyValue WithPropertyDescription:(JCPropertyDescription *)propertyDescription context:(JCObjectSerializerContext *)context {
    if(propertyValue != nil) {
        //if value is visited object return it with exit identifier
        if([context isVisitedObject:propertyValue]) {
            return [_objectIdentifyProvider identifierForObject:propertyValue];
        } else if([self isShouldVisitObjectType:propertyDescription.type]) {
            [context pushUnvisitedObject:propertyValue];
            return [_objectIdentifyProvider identifierForObject:propertyValue];
        } else if([propertyValue isKindOfClass:[NSArray class]] || [propertyValue isKindOfClass:[NSSet class]]) {
            NSMutableArray *arrayOfIdentifier = [NSMutableArray array];
            for(id value in propertyValue) {
                // if value type is array push to unvisited queue
                if([context isVisitedObject:value] == NO) {
                    [context pushUnvisitedObject:value];
                }
                
                [arrayOfIdentifier addObject:[_objectIdentifyProvider identifierForObject:value]];
            }
            propertyValue = arrayOfIdentifier;
        }
    }
    
    //TODO: (完成)Transform Value To Server Need
//    NSLog(@"%@",[[propertyDescription valueTransformer] transformedValue:propertyValue]);
    return [[propertyDescription valueTransformer] transformedValue:propertyValue];
}

//类实例变量值获取
- (id)valueWithIvarForObject:(NSObject *)object withPropertyDescription:(JCPropertyDescription *)propertyDescription context:(JCObjectSerializerContext *)context {
    NSParameterAssert(object != nil);
    NSParameterAssert(propertyDescription != nil);
    
    Ivar ivar = class_getInstanceVariable([object class], [propertyDescription.name UTF8String]);
    if (ivar) {
        const char *objCType = ivar_getTypeEncoding(ivar);
        
        ptrdiff_t ivarOffset = ivar_getOffset(ivar);
        const void *objectBaseAddress = (__bridge const void *)object;
        const void *ivarAddress = (((const uint8_t *)objectBaseAddress) + ivarOffset);
        
        switch (objCType[0])
        {
            case _C_ID:       return object_getIvar(object, ivar);
            case _C_CHR:      return @(*((char *)ivarAddress));
            case _C_UCHR:     return @(*((unsigned char *)ivarAddress));
            case _C_SHT:      return @(*((short *)ivarAddress));
            case _C_USHT:     return @(*((unsigned short *)ivarAddress));
            case _C_INT:      return @(*((int *)ivarAddress));
            case _C_UINT:     return @(*((unsigned int *)ivarAddress));
            case _C_LNG:      return @(*((long *)ivarAddress));
            case _C_ULNG:     return @(*((unsigned long *)ivarAddress));
            case _C_LNG_LNG:  return @(*((long long *)ivarAddress));
            case _C_ULNG_LNG: return @(*((unsigned long long *)ivarAddress));
            case _C_FLT:      return @(*((float *)ivarAddress));
            case _C_DBL:      return @(*((double *)ivarAddress));
            case _C_BOOL:     return @(*((_Bool *)ivarAddress));
            case _C_SEL:      return NSStringFromSelector(*((SEL*)ivarAddress));
            default:
                NSAssert(NO, @"Currently unsupported return type!");
                break;
        }
    }
    
    return nil;
}

// 类实例方法生成
- (NSInvocation *)invocationForObject:(id)object withSelectorDescription:(JCSelectorDescription *)selectorDescription {
    NSUInteger __unused parameterCount = [selectorDescription.parameters count];
    SEL aSelector = NSSelectorFromString(selectorDescription.selector);
    NSAssert(aSelector != nil, @"aSelector is nil");
    NSMethodSignature *methodSignature = [object methodSignatureForSelector:aSelector];
    NSInvocation *invocation = nil;
    if(methodSignature) {
        NSAssert([methodSignature numberOfArguments] == (parameterCount + 2), @"Unexpected number of arguments!");
        invocation = [NSInvocation invocationWithMethodSignature:methodSignature];
        invocation.selector = aSelector;
    }
    return invocation;
}

//类实例方法参数(参数最多一到两个,基本都为枚举类型,枚举类型将对每个枚举类型获取返回值)
- (NSArray *)parameterVariationsForPropertySelector:(JCSelectorDescription *)selectorDescription
{
    NSAssert([selectorDescription.parameters count] <= 1, @"Currently only support selectors that take 0 to 1 arguments.");
    
    NSMutableArray *variations = [[NSMutableArray alloc] init];
    
    // TODO: write an algorithm that generates all the variations of parameter combinations.
    if ([selectorDescription.parameters count] > 0) {
        JCSelectorParameters *parameterDescription = (selectorDescription.parameters)[0];
        for (id value in [self allValuesForType:parameterDescription.type]) {
            [variations addObject:@[ value ]];
        }
    } else {
        // An empty array of parameters (for methods that have no parameters).
        [variations addObject:@[]];
    }
    
    return [variations copy];
}
- (NSArray *)allValuesForType:(NSString *)typeName
{
    NSParameterAssert(typeName != nil);

    JCTypeDescription *typeDescription = [_configuration typeWithName:typeName];
    if ([typeDescription isKindOfClass:[JCEnumDescription class]]) {
        JCEnumDescription *enumDescription = (JCEnumDescription *)typeDescription;
        return [enumDescription allValues];
    }
    
    return @[];
}


- (BOOL)isShouldVisitObjectType:(NSString *)type {
    return [_configuration classWithName:type] != nil;
}
/**
 *  类继承层级
 */
- (NSArray *)classHierarchyArrayForObject:(NSObject *)object {
    NSMutableArray *classHierachy = [NSMutableArray array];
    Class class = [object class];
    while (class != nil) {
        [classHierachy addObject:NSStringFromClass(class)];
        class = [class superclass];
    }
    return [classHierachy copy];
}

/**
 *  根据Configure 搜索 Class/Superclass Configure
 */
- (JCClassDescription *)classDescriptionForObject:(NSObject *)object {
    NSParameterAssert(object != nil);
    Class class = [object class];
    
    while (class != nil) {
        JCClassDescription *classDescription = [_configuration classWithName:NSStringFromClass(class)];
        if(classDescription) {
            return classDescription;
        }
        class = [class superclass];
    }
    return nil;
    
}


@end
